/*
 * =====================================================================================
 *
 *       Filename:  rw_share_mem.c
 *
 *    Description:  interface fo share memory
 *
 *        Version:  1.0
 *        Created:  01/22/15 10:30:11
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  yangkun (yk)
 *          Email:  kun.yang@omnificweb.com
 *        Company:  omnificweb.com
 *
 * =====================================================================================
 */

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>
#include <sys/sem.h>
#include "rw_share_mem.h"

/*
 * note : check page size use command: getconf PAGESIZE
 * 
 */

struct mem_share
{
	int size;
	char buf[0];
};

//信号量数据结构
union semun {
    int value;
};

//用于映射信号量编号与对应意义的映射
enum MUTEX
{
    EMPTY,
    FILLED,
    RW_MUX,
    NUM_MUX
};

/* share memory id */
static int _share_mem_id=-1;
static struct mem_share *_p_mem_share=NULL;
static int	_mem_size=0; 
//信号量集合描述符
int semid;

//获取资源
//对对应信号量实施P操作
extern "C" void P(short unsigned int num)
{
    struct sembuf sb =
        {
            num, -1, 0 //0表示信号量编号,-1表示P操作,SEM_UNDO表示进程退出后，该进程对sem进行的操作将被撤销
        };
    //修改集合中，一个或多个信号量值
    semop(semid, &sb, 1);
}

//释放资源
//对对应信号量实施V操作
extern "C" void V(short unsigned int num)
{
    struct sembuf sb = {
        num, 1, 0 //
    };
    semop(semid, &sb, 1);
}

/*
 * desp		:	init share memory
 * parm		:	size,the size of share memory
 * return	:	0 success
 */
extern "C" int init_share_mem(int size)
{
	int page_size;
	int is_again=0;

	page_size=getpagesize();
	if(0 == size)
	{
		_mem_size=0;
	}
	else
	{
		_mem_size=((size+page_size+sizeof(struct mem_share))/page_size + 1) * page_size;
	}
	printf("_mem_size:%d\n",_mem_size);
again:
	//share_mem_id=shmget(SHARE_MEMORY_KEY,memsize, 0640);
	_share_mem_id=shmget(SHARE_MEMORY_KEY, _mem_size, 0666  | IPC_CREAT);
	if( -1 == _share_mem_id)
	{
		perror("init shmget error:");

		//size !=0 is write
		if(is_again==0 && size!=0 && errno==EINVAL)
		{
			is_again = 1;
			_share_mem_id=shmget(SHARE_MEMORY_KEY, _mem_size, IPC_CREAT|IPC_EXCL);	
			if(-1==_share_mem_id)
			{
				//just exist
				if (errno==EEXIST)
				{
					if(-1 == del_share_mem())
					{
						printf("delete share mem error\n");
						return -1;
					}
					//for write just try again
					goto again;
				}
				else
				{
					perror("1unknow error");
					return -1;
				}
			}
		}
		perror("2unknow error");
		return -1;
	}

	//get mem info
	struct shmid_ds shmds;
	if(0 != shmctl(_share_mem_id, IPC_STAT, &shmds))
	{
		perror("get mem info error");
	}
	_mem_size=shmds.shm_segsz;
	printf( "Size of memory segment is %d\n",shmds.shm_segsz );
	printf( "Numbre of attaches %d\n",( int )shmds.shm_nattch );

	//get share memory point
	_p_mem_share = (struct mem_share *)shmat(_share_mem_id, NULL, 0);
	if(_p_mem_share == (void *)-1)
	{
		perror("attach share memory error\n");
		printf("point:%p\n",_p_mem_share);
		return -1;
	}
	return 0;
}

/* 
 * desp		:	write data into share memory
 * parm		:	buf and bufsize
 * return	:	0 success,-1 error
 */
extern "C" int write_share_mem(char *buf,unsigned int bufsize)
{
	P(EMPTY);
    P(RW_MUX);
	if(_mem_size-sizeof(struct mem_share)<bufsize)
	{
		printf("the buf size is too large\n");
		return -1;
	}
	_p_mem_share->size = bufsize;
	memcpy(_p_mem_share->buf ,buf, bufsize);
	printf("write buf size %d success,membuf size:%d\n",
			bufsize, _p_mem_share->size);
	V(RW_MUX);
    V(FILLED);
	return 0;
}

/* 
 * desp		:	read data from share memory
 * parm		:	buf and bufsize
 * return 	:	0 success, >0 error reurn the memory share buf size
 */
extern "C" int read_share_mem(char *buf, unsigned int bufsize)
{
	P(FILLED);
    P(RW_MUX);
	if(_p_mem_share->size > bufsize)
	{
		printf("the buf size is too small,mem buf size:%d, bufsize:%d\n",
				_p_mem_share->size, bufsize);
		return _p_mem_share->size;
	}
	memcpy(buf, _p_mem_share->buf, _p_mem_share->size);
	printf("read buf size:%d\n", _p_mem_share->size);
	V(RW_MUX);
    V(EMPTY);
	return 0;
}

/*
 * desp		:	detach share memory (don,t delete it)
 * return 	:	0 success -1 error
 */
extern "C" int detach_share_mem()
{
	if(-1 == shmdt(_p_mem_share))
	{
		perror("detach share memory error");
		return -1;
	}
	return 0;
}

/*
 * desp		:	delete share memory
 * return 	:	0 success, -1 error
 */
extern "C" int del_share_mem()
{
	int shmid=shmget(SHARE_MEMORY_KEY, 0, IPC_CREAT);
	if(-1 == shmid)
	{
		perror("get shm id error");
		return -1;
	}
	if(shmctl(shmid, IPC_RMID, 0)<0)
	{
		perror("rm share memory error:");
		return -1;
	}
	return 0;
}

